#ifndef _GRAPH_EXAMINATOR_H_
#define _GRAPH_EXAMINATOR_H_

#include "Global.h"
#include "SequenceGraph.h"

/**
 * 	Graph examinator.
 * 	Examine basic adjacency information, twin information and if the graph is in ideal state.
 * 	The ideal state means no more possible merges can be done.
 *
 *		TODO: add redundant vertex test which means their contents are the same...
 * 
 * @author	Tao Yu
 * @date	2011/3/23
 */
class GraphExaminator {
public:
	static bool exam(SequenceGraph& g) {
		bool pass = true;
		g.resetCursor();
		while (g.hasNext()) {
			SequenceVertex* vertex = g.next();
			if ( !testEdgeInfo(g, vertex) )
			{
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "NULL edge info found.";
				pass = false;
			}
			if ( !testTwinInfo(g, vertex) )
			{
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "Twin Node: ";
				LOG(ERROR) << g.vertexInfoToString(vertex->getTwinVertex());
				LOG(ERROR) << "Wrong twin info found.";
				pass = false;
			}
			const vector<SequenceEdge>& preds =vertex->getPredecessors();
			const vector<SequenceEdge>& succs =vertex->getSuccessors();
			int size = preds.size();
			for (int i=0; i < size; ++i)
			{
				if (isInclude(preds[i].end->getSuccessors(), vertex) == false)
				{
					LOG(ERROR) << g.vertexInfoToString(vertex);
					LOG(ERROR) << "The predecessor details: ";
					LOG(ERROR) << g.vertexInfoToString(preds[i].end);
					LOG(ERROR) << "Predecessors do not have this vertex as successor.";
					pass = false;
				}
			}
			if (size == 1 && preds[0].end->numSuccessors() == 1 && vertex != preds[0].end) {
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "The predecessor details: ";
				LOG(ERROR) << g.vertexInfoToString(preds[0].end);
				LOG(ERROR) << "Broken vertex found. Should merge with the predecessor.";
				pass = false;
			}
			size = succs.size();
			for (int i=0; i < size; i++)
			{
				if (isInclude(succs[i].end->getPredecessors(), vertex) == false)
				{
					LOG(ERROR) << g.vertexInfoToString(vertex);
					LOG(ERROR) << "The successor details: ";
					LOG(ERROR) << g.vertexInfoToString(succs[0].end);
					LOG(ERROR) << "Successors do not have this vertex as predecessor.";
					pass = false;
				}
			}
			if (size == 1 && succs[0].end->numPredecessors() == 1 && vertex != succs[0].end) {
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "The successor details: ";
				LOG(ERROR) << g.vertexInfoToString(succs[0].end);
				LOG(ERROR) << "Broken vertex found. Should merge with the successor.";
				pass = false;
			}
		}
		if (pass) {
			LOG(INFO) << "All tests passed!";
		}
		else {
			LOG(ERROR) << "Some tests failed!";
		}
		return pass;
	}

	/**
	 * 	Query if  the collection includes given element.
	 *
	 * @author	Tao Yu
	 * @date	2011/3/23
	 *
	 * @param [in]	collection The collection.
	 * @param [in]	v				  	The element.
	 *
	 * @return	true if include, false if not.
	 */
	static bool isInclude(const vector<SequenceEdge>& collection, const SequenceVertex* v)
	{
		int n;
		n = collection.size();
		int i;
		for (i = 0; i < n; i++)
		{
			if (collection[i].end == v)
				return true;
		}
		return false;
	}

	/**
	 * 	Tests edge information for a given vertex.
	 *
	 * @author	Tao Yu
	 * @date	2011/3/23
	 *
	 * @param [in,out]	g	  	The graph of the vertex.
	 * @param [in,out]	vertex	If non-null, the vertex.
	 *
	 * @return	true if the test passes, false if the test fails.
	 */
	static bool testEdgeInfo(SequenceGraph& g, SequenceVertex *vertex) {
		if ( vertex == NULL ) { return false; }
		const vector<SequenceEdge>& succs = vertex->getSuccessors();
		int i = 0;
		for (vector<SequenceEdge>::const_iterator it = succs.begin(); it != succs.end(); ++it) {
			++i;
			if (!it->end) {
				LOG(ERROR) << "The " << i << "th successor is NULL";
				return false;
			}
		}
		const vector<SequenceEdge>& preds = vertex->getPredecessors();
		i = 0;
		for (vector<SequenceEdge>::const_iterator it = preds.begin(); it != preds.end(); ++it) {
			if (!it->end) {
				LOG(ERROR) << "The " <<i << "th predecessor is NULL";
				return false;
			}
		}
		return true;
	}

	/**
	 * 	Tests twin information for a given vertex.
	 *
	 * @author	Tao Yu
	 * @date	2011/3/23
	 *
	 * @param [in,out]	g	  	The graph of the vertex.
	 * @param [in,out]	vertex	If non-null, the vertex.
	 *
	 * @return	true if the test passes, false if the test fails.
	 */
	static bool testTwinInfo(SequenceGraph& g, SequenceVertex *vertex) {
		if ( vertex == NULL ) { return false; }
		SequenceVertex* twin = vertex->getTwinVertex();
		if ( vertex->getVertexID() != -(twin->getVertexID()) ||
			vertex->coverage() - twin->coverage() > 0.000001)
		{
			return false;
		}
		return true;
	}

	/**
	 * 	Tests adjacency information for a given vertex.
	 *
	 * @author	Tao Yu
	 * @date	2011/3/23
	 *
	 * @param [in,out]	g	  	The graph of the vertex.
	 * @param [in,out]	vertex	If non-null, the vertex.
	 *
	 * @return	true if the test passes, false if the test fails.
	 */
	static bool testAdjInfo(SequenceGraph& g, SequenceVertex* vertex) {
		if ( vertex == NULL ) { return false; }
		const vector<SequenceEdge>& preds =vertex->getPredecessors();
		const vector<SequenceEdge>& succs =vertex->getSuccessors();
		bool pass = true;
		int size = preds.size();
		for (int i=0; i < size; ++i)
		{
			if (isInclude(preds[i].end->getSuccessors(), vertex) == false)
			{
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "The predecessor details: ";
				LOG(ERROR) << g.vertexInfoToString(preds[i].end);
				LOG(ERROR) << "Predecessors do not have this vertex as successor.";
				pass = false;
			}
		}
		size = succs.size();
		for (int i=0; i < size; i++)
		{
			if (isInclude(succs[i].end->getPredecessors(), vertex) == false)
			{
				LOG(ERROR) << g.vertexInfoToString(vertex);
				LOG(ERROR) << "The successor details: ";
				LOG(ERROR) << g.vertexInfoToString(succs[0].end);
				LOG(ERROR) << "Successors do not have this vertex as predecessor.";
				pass = false;
			}
		}
		return pass;
	}
};

#endif
